<?xml version="1.0" encoding="UTF-8"?>
<!--
  Copyright 2010 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:ev="http://www.w3.org/2001/xml-events" version="1.0">
  <!-- Copyright 2010 Google Inc. All rights reserved. -->
  <!-- Author: jacoby@google.com (Jacoby Thwaites) -->
  <!-- Renders SVG, which works in Chrome if you use XHTML -->

  <!-- Here is the kind of op element we're formatting
    <op id="{1276,187519,999897}" opType="request_reply" isComposite="false">
      <mask>
        <field name="foo" id="{1244,221643,3321}"/>
      </mask>
      <input>
        <field name="n" id="{1276,187439,791034}"/>
        <field name="div" id="{1276,187448,822856}"/>
      </input>
      <outputs>
        <output name="Yes">
          <field name="YES" id="{1276,187456,247174}"/>
        </output>
        <output name="Maybe">
          <field name="MAYBE" id="{1276,187470,279034}"/>
        </output>
        <output name="No">
          <field name="NO" id="{1276,187463,624007}"/>
        </output>
      </outputs>
    </op>
  -->

  <xsl:param name="opWidthPx" select="40"/>
  <xsl:param name="opCornerPx" select="4"/>
  <xsl:param name="arrowWidthPx" select="22"/>
  <xsl:param name="setLabelIndentPx" select="2"/>

  <!-- Make this match the CSS rect stroke width -->
  <xsl:param name="opStrokePx" select="2"/>
  <xsl:param name="unitHeightPx" select="20"/>
  <xsl:param name="drawWidthPx" select="200"/>
  <xsl:param name="marginPx" select="10"/>

  <!-- Draw a null operation -->
  <xsl:template match="op[@opType = 'null']" mode="draw">

    <!-- Draw a single op inside a div -->
    <div class="op-{@opType}">
      <svg:svg version="1.1" baseProfile="full" width="{$drawWidthPx}"
        height="{$unitHeightPx*3}" zoomAndPan="magnify">
        <svg:g class="op" transform="translate({$marginPx}, {$marginPx})">

          <!-- Draw rectangle -->
          <svg:rect width="{$opWidthPx}" height="{$unitHeightPx*2}"
            rx="{$opCornerPx}" ry="{$opCornerPx}" />

          <!-- Draw empty set symbol -->
          <svg:text class="setLabel" x="{$opWidthPx div 2}" y="{$unitHeightPx}">
            <xsl:text>∅</xsl:text>
          </svg:text>
        </svg:g>
      </svg:svg>
    </div>
  </xsl:template>

  <!-- Draw an operation if it has any input or output fields -->
  <xsl:template match="op[@opType != 'null']" mode="draw">

    <!-- Vars to compute op height when drawn -->
    <xsl:variable name="mSCount" select="count(mask[field])"/>
    <xsl:variable name="mFCount" select="count(mask/field)"/>
    <xsl:variable name="iSCount" select="count(input[field])"/>
    <xsl:variable name="iFCount" select="count(input/field)"/>
    <xsl:variable name="oSCount" select="count(outputs/output[field])"/>
    <xsl:variable name="oFCount" select="count(outputs/output/field)"/>

    <!-- Aggregate to compute pixel height for each fieldset -->
    <xsl:variable name="maskHeight" select="$unitHeightPx*($mSCount+$mFCount)"/>
    <xsl:variable name="inputHeight" select="$unitHeightPx*($iSCount+$iFCount)"/>
    <xsl:variable name="outputsHeight" select="$unitHeightPx*($oSCount+$oFCount)"/>

    <!-- Compute offsets including width of the op border stroke -->
    <xsl:variable name="inputOffset" select="$maskHeight+($mSCount*$opStrokePx)"/>
    <xsl:variable name="outputsOffset" select="$inputOffset+$inputHeight+($iSCount*$opStrokePx)"/>

    <!--  Compute total height including margin top and bottom -->
    <xsl:variable name="drawHeightPx"
      select="2*$marginPx+$inputOffset+$outputsOffset+$outputsHeight"/>

    <!-- Draw a single op inside a div -->
    <div class="op-{@opType}">
      <svg:svg version="1.1" baseProfile="full" width="{$drawWidthPx}"
        height="{$drawHeightPx}" zoomAndPan="magnify">
        <xsl:call-template name="svg-defs"/>
        <svg:g class="op"
          transform="translate({$marginPx}, {$marginPx})">

          <!-- Draw 0 or 1 mask set -->
          <xsl:for-each select="mask[field]">
            <xsl:call-template name="draw-mask"/>
          </xsl:for-each>

          <!-- Draw 0 or 1 input set -->
          <xsl:for-each select="input[field]">
            <svg:g transform="translate(0,{$inputOffset})">
              <xsl:call-template name="draw-input"/>
            </svg:g>
          </xsl:for-each>

          <!-- Draw 0 or more output sets -->
          <svg:g class="outputs"
            transform=
            "translate(0,{$outputsOffset})">
            <xsl:for-each select="outputs/output[field]">
              <xsl:call-template name="draw-output"/>
            </xsl:for-each>
          </svg:g>
        </svg:g>
      </svg:svg>
    </div>
  </xsl:template>

  <!-- Draw mask set -->
  <xsl:template name="draw-mask">
    <svg:g class="mask">

      <!-- Draw rectangle -->
      <svg:rect
        width="{$opWidthPx}"
        height="{$unitHeightPx*(1+count(field))}"
        rx="{$opCornerPx}" ry="{$opCornerPx}"/>

      <!-- Mask set label -->
      <svg:text x="{$opWidthPx+$setLabelIndentPx}" y="1em">Mask</svg:text>

      <!-- Draw fields -->
      <svg:g class="fields">
        <xsl:apply-templates select="field" mode="draw">
          <xsl:sort select="@name" data-type="text" case-order="upper-first"/>
        </xsl:apply-templates>
      </svg:g>
    </svg:g>
  </xsl:template>

  <!-- Draw input set -->
  <xsl:template name="draw-input">
    <svg:g class="input">

      <!-- Draw rectangle -->
      <svg:rect
        width="{$opWidthPx}"
        height="{$unitHeightPx*(1+count(field))}"
        rx="{$opCornerPx}" ry="{$opCornerPx}"/>

      <!-- Input set label -->
      <svg:text x="{$opWidthPx+$setLabelIndentPx}" y="1em">Input</svg:text>

      <!-- Draw fields -->
      <svg:g class="fields">
        <xsl:apply-templates select="field" mode="draw">
          <xsl:sort select="@name" data-type="text" case-order="upper-first"/>
        </xsl:apply-templates>
      </svg:g>
    </svg:g>
  </xsl:template>

  <!-- Draw field. Checks local-name(parent::*) for input or output -->
  <xsl:template match="field" mode="draw">
    <xsl:variable name="dy" select="$unitHeightPx * position()"/>

     <!-- Arrow -->
    <svg:use xlink:href="#{local-name(parent::*)}Arrow"
      x="{$opWidthPx}" y="{$dy}"/>

    <!-- Label -->
    <svg:text class="{generate-id(key('fieldIndex',@id))}"
      x="{$opWidthPx+$arrowWidthPx+$marginPx}"
      y="{$dy}">
      <xsl:value-of select="@name"/>
    </svg:text>
  </xsl:template>

  <!-- Draw output set -->
  <xsl:template name="draw-output">

    <!-- Compute offset and height -->
    <xsl:variable name="dy"
      select="$unitHeightPx*(count(preceding-sibling::output)+count(preceding-sibling::output/field))"/>
    <xsl:variable name="dh" select="1+count(field)"/>

    <!-- Draw output set -->
    <svg:g class="output" transform="translate(0, {$dy})">
      <svg:rect
        width="{$opWidthPx}"
        height="{$unitHeightPx*$dh}"
        rx="{$opCornerPx}" ry="{$opCornerPx}"/>

      <!-- Output set name -->
      <svg:text x="{$opWidthPx + $setLabelIndentPx}" y="1em">
        <xsl:value-of select="@name"/>
      </svg:text>

      <!-- Iterative output box is marked with a suitable symbol -->
      <xsl:if test="ancestor::op/@opType='iterative_request_reply'">
        <svg:text class="setLabel"
        x="{$opWidthPx div 2}" y="{$unitHeightPx*$dh div 2}">
        <xsl:text>∞</xsl:text>
        </svg:text>
      </xsl:if>

      <!-- Draw fields -->
      <svg:g class="fields">
        <xsl:apply-templates select="field" mode="draw">
          <xsl:sort select="@name" data-type="text" case-order="upper-first"/>
        </xsl:apply-templates>
      </svg:g>
    </svg:g>
  </xsl:template>

  <!-- SVG defs -->
  <xsl:template name="svg-defs">
    <svg:defs>
      <svg:g id="maskArrow">
        <svg:circle cx="5" cy="0" r="5"/>
        <svg:path d="M10,0h12"/>
      </svg:g>

      <svg:g id="inputArrow">
        <svg:path d="M2,0h20M0,0l8,5M0,0l8,-5"/>
      </svg:g>

      <svg:g id="outputArrow">
        <svg:path d="M0,0h20M22,0l-8,5M22,0l-8,-5"/>
      </svg:g>
    </svg:defs>
  </xsl:template>

</xsl:stylesheet>
